package com.bins.mongodb.dao;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

public abstract class MongoDbDao<T> {

	protected abstract Class<T> getEntityClass();

	@Autowired
	private MongoTemplate mongoTemplate;

	public void save(T t) {
		this.mongoTemplate.save(t);
	}
	
	public List<T> findAll(Class<T> entityClass) {
		return this.mongoTemplate.findAll(entityClass);
	}

	public T queryById(Long id) {
		Query query = new Query(Criteria.where("_id").is(id));
		return this.mongoTemplate.findOne(query, this.getEntityClass());
	}

	public List<T> queryList(T object) {
		Query query = getQueryByObject(object);
		return mongoTemplate.find(query, this.getEntityClass());
	}

	public T queryOne(T object) {
		Query query = getQueryByObject(object);
		return mongoTemplate.findOne(query, this.getEntityClass());
	}

	public List<T> getPage(T object, int start, int size) {
		Query query = getQueryByObject(object);
		query.skip(start);
		query.limit(size);
		return this.mongoTemplate.find(query, this.getEntityClass());
	}

	public Long getCount(T object) {
		Query query = getQueryByObject(object);
		return this.mongoTemplate.count(query, this.getEntityClass());
	}

	public int delete(T t) {
		return (int) this.mongoTemplate.remove(t).getDeletedCount();
	}

	public void deleteById(Long id) {
		Criteria criteria = Criteria.where("_id").is(id);
		if (null != criteria) {
			Query query = new Query(criteria);
			T obj = this.mongoTemplate.findOne(query, this.getEntityClass());
			if (obj != null) {
				this.delete(obj);
			}
		}
	}

	public void updateFirst(T srcObj, T targetObj) {
		Query query = getQueryByObject(srcObj);
		Update update = getUpdateByObject(targetObj);
		this.mongoTemplate.updateFirst(query, update, this.getEntityClass());
	}

	public void updateMulti(T srcObj, T targetObj) {
		Query query = getQueryByObject(srcObj);
		Update update = getUpdateByObject(targetObj);
		this.mongoTemplate.updateMulti(query, update, this.getEntityClass());
	}

	public void updateInsert(T srcObj, T targetObj) {
		Query query = getQueryByObject(srcObj);
		Update update = getUpdateByObject(targetObj);
		this.mongoTemplate.upsert(query, update, this.getEntityClass());
	}
	
	public void updateFirst(Query query, Update update, Class<T> entityClass) {
		this.mongoTemplate.updateFirst(query, update, entityClass);
	}

	private Query getQueryByObject(T object) {
		Query query = new Query();
		String[] fileds = getFiledName(object);
		Criteria criteria = new Criteria();
		for (int i = 0; i < fileds.length; i++) {
			String filedName = (String) fileds[i];
			Object filedValue = getFieldValueByName(filedName, object);
			if (filedValue != null) {
				criteria.and(filedName).is(filedValue);
			}
		}
		query.addCriteria(criteria);
		return query;
	}

	private Update getUpdateByObject(T object) {
		Update update = new Update();
		String[] fileds = getFiledName(object);
		for (int i = 0; i < fileds.length; i++) {
			String filedName = (String) fileds[i];
			Object filedValue = getFieldValueByName(filedName, object);
			if (filedValue != null) {
				update.set(filedName, filedValue);
			}
		}
		return update;
	}

	private static String[] getFiledName(Object o) {
		Field[] fields = o.getClass().getDeclaredFields();
		String[] fieldNames = new String[fields.length];
		for (int i = 0; i < fields.length; ++i) {
			fieldNames[i] = fields[i].getName();
		}
		return fieldNames;
	}

	private static Object getFieldValueByName(String fieldName, Object o) {
		try {
			String e = fieldName.substring(0, 1).toUpperCase();
			String getter = "get" + e + fieldName.substring(1);
			Method method = o.getClass().getMethod(getter, new Class[0]);
			return method.invoke(o, new Object[0]);
		} catch (Exception var6) {
			return null;
		}
	}
}